#pragma once
#include "../mqcommon/helper.hpp"
#include "../mqcommon/logger.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>
#include <vector>
#include <mutex>
#include <sstream>
#include <list>
namespace wzf
{
    #define DATAFILE_SUBFIX ".mqp";
    #define TMPFILE_SUBFIX ".mqp.map";
    using MessagePtr = std::shared_ptr<wzf::Message>;
    class MessageMapper
    {
    public:
        MessageMapper(const std::string dir, const std::string qname)
            :_qname(qname)
        {
            if(dir.back() != '/')
            {
                _datafile = dir + "/";
                _tmpfile = dir + "/";
            }
            _datafile += qname + DATAFILE_SUBFIX; 
            _tmpfile += qname + TMPFILE_SUBFIX;
            FileHelper::createDirectory(dir);
            CreateMsgFile();
        }

        bool CreateMsgFile() 
        {
            return FileHelper::createFile(_datafile) && FileHelper::createFile(_tmpfile);
        }

        bool RemoveMsgFile()
        {
            return FileHelper::removeFile(_datafile) && FileHelper::removeFile(_tmpfile);
        }

        void Insert(MessagePtr& msg, const std::string filename) //消息保存到磁盘文件中
        {
            //1.序列化消息中的payload
            std::string buffer = msg->payload().SerializeAsString();
    
            //2. 保存到文件的末尾
            //2.1 找到文件的末尾
            FileHelper helper(filename);
            size_t fsize = helper.size();
            //2.2写入
            helper.write(buffer.c_str(), fsize, buffer.size());
    
            //3.更新msg
            msg->set_offset(fsize);
            msg->set_length(buffer.size());
        } 

        void Insert_datafile(MessagePtr& msg) //消息保存到磁盘文件中
        {
            Insert(msg, _datafile);
        } 

        void Insert_tmpfile(MessagePtr& msg) //消息保存到磁盘文件中
        {
            Insert(msg, _tmpfile);
        } 

        void Remove(MessagePtr& msg) //磁盘文件中对应消息的valid置"0"
        {
            msg->mutable_payload()->set_valid("0");

            std::string buffer = msg->payload().SerializeAsString();
            if(buffer.size() != msg->length())
            {
                ELOG("与原文件大小不一致");
                return;
            }
            FileHelper helper(_datafile);
            helper.write(buffer.c_str(), msg->offset(), buffer.size());
        }
        
        std::list<MessagePtr> gc()  //回收机制
        {
            std::list<MessagePtr> result;
            Load(result);
            //写入磁盘
            FileHelper::createFile(_tmpfile);
            for(auto& msg : result)
                Insert_tmpfile(msg);
            //删除原文件，创建
            FileHelper::removeFile(_datafile);      //这两个调的都是库里的，会错吗？如果没有tmpfile，猜测下面这个会错
            FileHelper(_tmpfile).rename(_datafile);

            return result;
        }

        void Load(std::list<MessagePtr>& result)
        {
            FileHelper helper(_datafile);
            size_t fsize = helper.size();
            size_t offset = 0;
            //放到内存里
            while(offset < fsize)
            { 
                size_t msg_size = 0;
                helper.read((char*)(&msg_size), offset, 4);
                offset += 4;

                std::string msg_buffer(msg_size, 0);
                helper.read(&msg_buffer[0], offset, msg_size);
                offset += msg_size; 
                //反序列化到对象里
                MessagePtr msg = std::make_shared<Message>();
                msg->ParseFromString(msg_buffer);

                if(msg->payload().valid() != "0")
                    result.push_back(msg);
            }
        }
    private:
        std::string _qname;
        std::string _datafile;
        std::string _tmpfile;
    };

    class QueueMessage
    {
    public:
        QueueMessage(const std::string& dir, const std::string& qname)
            : _qname(qname), _valid_count(0), _total_count(0), _mapper(dir, qname)
        {

        }
        bool Insert(const BasicProperties* bp, const std::string& body, DeliveryMode mode)
        {
            MessagePtr ptr = std::make_shared<Message>();
            //填消息
            ptr->mutable_payload()->set_body(body);
            //填属性
            if(bp)
            {
                ptr->mutable_payload()->mutable_properties()->set_id(bp->id());
                ptr->mutable_payload()->mutable_properties()->set_delivery_mode(bp->delivery_mode()); 
                ptr->mutable_payload()->mutable_properties()->set_routing_key(bp->routing_key()); 
            }
            else
            {
                ptr->mutable_payload()->mutable_properties()->set_id(wzf::UUIDHelper::uuid());
                ptr->mutable_payload()->mutable_properties()->set_delivery_mode(mode); 
                ptr->mutable_payload()->mutable_properties()->set_routing_key(""); 
            }
            //持久化存储
            std::unique_lock<std::mutex> lock(_mutex);
            ptr->mutable_payload()->set_valid("1"); 

            if(ptr->mutable_payload()->mutable_properties()->delivery_mode() == DURABLE)
            {
                // ptr->mutable_payload()->set_valid("1");  不应该放在里面
                _mapper.Insert_datafile(ptr); //这里面会填入offset, length
                _durable_msgs.insert({ptr->mutable_payload()->mutable_properties()->id(), ptr});
                _valid_count++;
                _total_count++;
            }
            //内存管理
            _msgs.push_back(ptr);

            return true;
        }
        bool Remove(const std::string& msg_id)   //顺便做磁盘数据的回收 //确认消息
        {
            std::unique_lock<std::mutex> lock(_mutex);
            //确认队列中查找是否有
            if(!_waitack_msgs.count(msg_id))
                return false;
            if(_waitack_msgs[msg_id]->payload().properties().delivery_mode() == DURABLE)
            {
                //删掉磁盘里的
                _mapper.Remove(_waitack_msgs[msg_id]);
                //删掉内存中的
                _durable_msgs.erase(msg_id);
                _valid_count--;
                if(_total_count > 2000 && _valid_count * 2 < _total_count)   //磁盘回收无效消息
                {
                    Update();
                //update内存中的持久化消息，因为此时offset已经改变了,_valid_count,_total_count也改变了
                }
            }
            //删掉内存中的
            _waitack_msgs.erase(msg_id);
        }
    private:
        void Update()
        {
            std::list<MessagePtr> msgs = _mapper.gc();   //磁盘里
            for(auto& msg : msgs)
            {
                auto it = _durable_msgs.find(msg->payload().properties().id());
                if(it == _durable_msgs.end())
                {
                    DLOG("有消息没有被管理起来");
                    _msgs.push_back(msg);
                    _durable_msgs.insert({msg->payload().properties().id(), msg});
                }
                else
                {
                    it->second->set_offset(msg->offset());
                    it->second->set_length(msg->length()); //我觉得这个length是不会变的
                }
            }
            _valid_count = msgs.size();
            _total_count = msgs.size();
        }
    public:
        MessagePtr front()   //取出消息，不会立刻删掉，会存储到应答的hash里
        {
            std::unique_lock<std::mutex> lock(_mutex);
            if(!_msgs.empty())
            {
                MessagePtr ptr = _msgs.front();
                _msgs.pop_front();
                _waitack_msgs.insert({ptr->payload().properties().id(), ptr});
                return ptr;
            }
            return nullptr;
        }
        size_t getable_count()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _msgs.size();
        }
        size_t valid_count()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _valid_count;
        }
        size_t total_count()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _total_count;
        }
        size_t durable_count()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _durable_msgs.size();
        }
        size_t waitack_count()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            return _waitack_msgs.size();
        }
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            _mapper.RemoveMsgFile();
            _msgs.clear();
            _durable_msgs.clear();
            _waitack_msgs.clear();
            _valid_count = 0;
            _total_count = 0;
        }

    private:
        std::mutex _mutex;
        std::string _qname;
        size_t _valid_count;            //有效消息数量
        size_t _total_count;            //持久化的消息的数量（有效，无效）
        MessageMapper _mapper;
        std::list<MessagePtr> _msgs;    //待推送消息
        std::unordered_map<std::string, MessagePtr> _durable_msgs;  //持久化消息
        std::unordered_map<std::string, MessagePtr> _waitack_msgs;  //待确认消息
    };
}